local util = require "util"
local error_code = require "error_code"
local shape_common = require "shape_common"
local constant = require "constant"

local table_insert = table.insert
local math_floor = math.floor

local math_random = math.random
--math.randomseed(os.time())

--游戏状态定义
local GAME_STATUS_INIT = 0          --初始化状态
local GAME_STATUS_STARTED = 1       --游戏开始
local GAME_STATUS_BETTING = 2       --押注
local GAME_STATUS_OVER = 3          --结束状态

local MAX_PLAYER_NUM = 1
local MIN_PLAYER_NUM = 1


local TIGER_INDEX = {}
local M = {}

function TIGER_INDEX:init(curr_player_uid)
    self.game_status = GAME_STATUS_INIT
    self.player_betting_map = {}            --当前游戏中的玩家列表，老虎机目前一个桌子上只有一个玩家
    self.curr_shape = {}                    --当前使用的图形及对应参数
    self.game_result = {}
    self.seven_num = 0                      --有几个7形成了连线
    self.player_caijin_coins = 0            --玩家可以获得的彩金值
    self.curr_player_uid = curr_player_uid  --当前玩家
    self.is_free = false                    --当局是否使用免费次数下注
end

function TIGER_INDEX:start_game()
    assert(self.game_status == GAME_STATUS_INIT)
    self.game_status = GAME_STATUS_STARTED

    return true
end

function TIGER_INDEX:start_betting()
    assert(self.game_status == GAME_STATUS_STARTED)
    self.game_status = GAME_STATUS_BETTING
    return true
end

function TIGER_INDEX:is_free_times()
    return self.is_free
end

function TIGER_INDEX:score_isvalid(is_free,score,last_bet_score)
    --免费下注时需判断客户端发过来的下注与服务器是否一致
    if is_free then
        if score ~= last_bet_score then
            print("=========score ~= last_bet_score===",score,last_bet_score)
            return false
        end
    end

    --检测下注范围是否落在有效范围内
    local bet_range = global_configs.tiger_rate.bet_range
    for k, v in pairs(bet_range) do
        if score == v then
            return true
        end
    end
    
    return false
end

function TIGER_INDEX:bet_lines_isvalid(is_free,bet_lines,last_bet_line)
    --免费下注时需判断客户端发过来的压线与服务器是否一致
    if is_free then
        if bet_lines ~= last_bet_line then
            print("=========bet_lines ~= last_bet_line===",bet_lines,last_bet_line)
            return false
        end
    end

    local award_lines = global_configs.fruit_rand_shape.award_lines --可以获奖的连线
    if bet_lines > 0 and bet_lines <= #award_lines then
        return true
    end

    return false
end

function TIGER_INDEX:can_bet(uid,is_free,score,bet_lines,last_bet_score,last_bet_line)
    --检查状态
    if self.game_status ~= GAME_STATUS_BETTING then
        return false, error_code.BET_DEADLINE_ERROR
    end
    if uid ~= self.curr_player_uid then
        errlog("======uid ~= self.curr_player_uid=",uid,self.curr_player_uid)
        return false,-301
    end
    --判断当前玩家下注的金额数是否合法
    if not self:score_isvalid(is_free,score,last_bet_score) then
        errlog(uid, 'not TIGER_INDEX:score_isvalid(uid,score)',score)
        return false, error_code.BET_SELECT_NUMBER_ERROR
    end

    --判断当前玩家压线数是否合法
    if not self:bet_lines_isvalid(is_free,bet_lines,last_bet_line) then
        errlog(uid, 'not TIGER_INDEX:bet_lines_isvalid(uid,bet_lines)',bet_lines)
        return false, error_code.BET_LINES_ERROR
    end

    local player_betting_map = self.player_betting_map
    local records = player_betting_map[uid]
    if records then                 --防止玩家连续点下注
        errlog(uid, 'player is betting')
        return false, -304
    end

    return true
end

function TIGER_INDEX:had_betted(uid)
    return self.player_betting_map[uid]
end

function TIGER_INDEX:bet(uid,is_free,score,bet_lines,last_bet_score,last_bet_line)
    local ok,ret = self:can_bet(uid,is_free,score,bet_lines,last_bet_score,last_bet_line)
    if not ok then
        return false,ret
    end

    local player_betting_map = self.player_betting_map
    local records = player_betting_map[uid]
    
    records = {}
    player_betting_map[uid] = records

    records.bet_lines = bet_lines
    records.bet_score = score 
  
    if is_free then
        self.is_free = true
    end

    return true,{
        bet_score = records.bet_score
    }
end

function TIGER_INDEX:get_shape_info(shape,bet_lines)
    local line_rate = global_configs.fruit_rand_shape.line_rate  --图案倍数
    local award_lines = global_configs.fruit_rand_shape.award_lines --可以获奖的连线
    local caijin_rate = global_configs.tiger_rate.caijin_rate
    local lines = shape_common.check_line(shape,line_rate,award_lines,caijin_rate)
    local times,free_times,total_caijin_rate = shape_common.get_total_times(bet_lines,lines)
    local others = {
        times = times,
        free_times = free_times,
        total_caijin_rate = total_caijin_rate
    }
    return lines,others
end

function TIGER_INDEX:create_shape(produce_rate)
    local total_weight_map = shape_common.get_total_weight(produce_rate)
    local one_shape = shape_common.init_one_shape(total_weight_map,produce_rate)	
    
    return one_shape
end

function TIGER_INDEX:is_store_nomal(store_coins,others,result)--发放奖励后库存是否为正常值
    local sys_warn = global_configs.fruit_store.store_base_sys.base_min or 0         --库存1下限
    local adjust_warn = global_configs.fruit_store.store_adjust.award_min or 0        --库存2下限
    local jackpot_warn = global_configs.fruit_store.store_real_jackpot.base_warn or 0 --奖池警报值

    local bet_score,bet_lines = self:get_uid_bet_record(self.curr_player_uid)
    local caijin_lose = math_floor((others.total_caijin_rate * store_coins.jackpot_coins))    --奖池金额
    local lines_lose = math_floor(others.times * bet_score)    --基础连线金额
    local total_lose = caijin_lose + lines_lose    --总金额
    if result==constant.AWARD_STORE_LOSE then      --从库存2扣钱
        if total_lose == 0 then
            return true
        end
        if store_coins.adjust_coins-total_lose <= adjust_warn then
            print("===========award store not nomal====",store_coins.adjust_coins,total_lose,adjust_warn)
            return false
        end
    elseif store_result == constant.JACKPOT_STORE then  --奖池部分从奖池扣，基础连线从库存1扣
        if total_lose == 0 then
            return true
        end --奖池可以到达警报值
        -- if store_coins.jackpot_coins - caijin_lose <= jackpot_warn then
        --     print("===========jackpot not nomal====",store_coins.jackpot_coins,caijin_lose,jackpot_warn)
        --     return false
        -- end
        if store_coins.base_coins - lines_lose <= sys_warn then
            print("===========base store not nomal====",result,store_coins.base_coins,lines_lose,sys_warn)
            return false
        end
    else     --从库存1扣钱(个人控制,库存1(基础库存)控制,免费次数)
        if lines_lose == 0 then
            return true
        end
        if store_coins.base_coins - lines_lose <= sys_warn then   --此种情况不可能获得奖金caijin_lose==0
            print("===========base store not nomal====",result,store_coins.base_coins,caijin_lose,lines_lose,sys_warn)
            return false
        end
    end
    
    return true
end

function TIGER_INDEX:get_new_shape(bet_lines,store_coins,line_shape,result)
    --若随机到Jackpot，免费次数，则重新随机，直到出符合要求的图形
    --如果在正常范围则直接发放，如库存不在正常范围则重新随机。
    local produce_rate = global_configs.fruit_rand_shape.shape_rate --生成每张图的概率
    local one_shape = self:create_shape(produce_rate)
    local lines,others = self:get_shape_info(one_shape,bet_lines)
    local type = shape_common.get_one_shape_type(lines,bet_lines)
    local bet_score,bet_lines = self:get_uid_bet_record(self.curr_player_uid)

    local round = 0
    print("==============before create new shape====",type,others.times,lose_coins,tostring_r(store_coins))
    while (type == shape_common.JACKPOT or type == shape_common.FREE_TIMES or others.free_times > 0 or others.total_caijin_rate > 0 or not self:is_store_nomal(store_coins,others,result)) and round < 10 do
        one_shape = self:create_shape(produce_rate)
        lines,others = self:get_shape_info(one_shape,bet_lines)
        type = shape_common.get_one_shape_type(lines,bet_lines)

        round = round + 1
    end
    if type == shape_common.JACKPOT or others.total_caijin_rate > 0 or type == shape_common.FREE_TIMES or others.free_times > 0 or not self:is_store_nomal(store_coins,others,result) then --循环十次还没找到合适的，从shapedata(内存)中选一张
        local rand_tab = {}
        for i = 1, shape_common.MAX_TYPE-1 do
            if line_shape[i] and #line_shape[i] > 0 then
                table_insert(rand_tab,i)
            end
        end
        if #rand_tab > 0 then      
            for _,v in pairs(rand_tab) do   --从大亏类开始找吧,保证库存正常
                local type_shape = line_shape[v]     --某条线某种类型的图{}
                local rand_shape = type_shape[math_random(1,#type_shape)]
                if rand_shape then
                    one_shape = rand_shape
                    lines,others = self:get_shape_info(one_shape,bet_lines)
                    type = shape_common.get_one_shape_type(lines,bet_lines)
                    break
                end
            end
        end
    end
    print("===========after create new shape=====",round,type,result,tostring_r(one_shape),tostring_r(lines),tostring_r(others))
    
    return one_shape,lines,others
end

function TIGER_INDEX:select_shape_type(line_shape,type1,type2,rate)
    local select_type
    print("================11111111111111",type1,type2,#line_shape[type1],#line_shape[type2])

    if line_shape[type1] and line_shape[type2] and #line_shape[type1] > 0 and #line_shape[type2] > 0 then
        if math_random(1,(rate[1]+rate[2])) <= rate[1] then
            select_type = type1
        else
            select_type = type2
        end
    elseif line_shape[type1] and line_shape[type2] and #line_shape[type1] > 0 and #line_shape[type2] <= 0 then
        select_type = type1
    elseif line_shape[type1] and line_shape[type2] and #line_shape[type1] <= 0 and #line_shape[type2] > 0 then
        select_type = type2
    end
    print("========select_shape_type==",select_type)
    return select_type
end

function TIGER_INDEX:set_curr_shape(curr_shape,bet_lines)
    print("=========111155555set_curr_shape=",bet_lines,tostring_r(curr_shape))
    local one_shape = {}
    local curr_lines = {}
    for lines, line_nums in pairs(curr_shape.shape) do
        local shape_lines = {}
        for row,value in pairs(line_nums) do
            table_insert(shape_lines,value)
        end
        table_insert(one_shape,{shape = shape_lines})
    end

    for _,v in pairs(curr_shape.lines) do
        if v.line_num <= bet_lines then
            local line_times = v.line_times
            if v.fruit == shape_common.SEVEN then     --奖金时，line_times为0
                line_times = 0
            end
            table_insert(curr_lines,{shape_num = v.line_num,fruit_type = v.fruit,fruit_num = v.len,line_times = line_times})
        end
    end

    self.curr_shape = {
        shape = one_shape, 
        total_times = curr_shape.others.times,
        lines = curr_lines,
        free_times = curr_shape.others.free_times,
        jackpot_rate = curr_shape.others.total_caijin_rate,
        type = shape_common.get_one_shape_type(curr_shape.lines,bet_lines)
    }
end

function TIGER_INDEX:select_shape(result,uid,store_coins)
    local err_result = 0 
    print("===========TIGER_INDEX:select_shape====",uid,self.curr_player_uid,result,tostring_r(store_coins))
    if uid ~= self.curr_player_uid then
        errlog(uid,"uid ~= self.curr_player_uid",self.curr_player_uid)
        return
    end
    local bet_score,bet_lines = self:get_uid_bet_record(uid)
    if not bet_lines or not bet_score then
        errlog(uid,"player not bet",self.curr_player_uid)
        return
    end
    local curr_shape
    local personal_win_rate = global_configs.fruit_rand_shape.personal_win_rate or {50,50}
    local personal_lose_rate = global_configs.fruit_rand_shape.personal_lose_rate or {50,50}
    local award_win_rate = global_configs.fruit_rand_shape.award_win_rate or {50,50}
    local system_lose_rate = global_configs.fruit_rand_shape.system_lose_rate or {50,50}
    local one_shape,lines,others
    local shape_map = shapedata
    local line_shape = shape_map[bet_lines]
    local select_type

    if result == constant.PERSONAL_STORE_WIN then      --小赢和大赢中取
        select_type = self:select_shape_type(line_shape,shape_common.SMALL_WIN,shape_common.BIG_WIN,personal_win_rate)
    elseif result == constant.PERSONAL_STORE_LOSE then  --从小亏和大亏中取图案
        select_type = self:select_shape_type(line_shape,shape_common.SMALL_LOSE,shape_common.BIG_LOSE,personal_lose_rate)
    elseif result == constant.JACKPOT_STORE then        --奖池中取
        select_type = shape_common.JACKPOT
    elseif result == constant.AWARD_STORE_LOSE then     --从“小赢”和“大赢”的结果集中取
        select_type = self:select_shape_type(line_shape,shape_common.SMALL_WIN,shape_common.BIG_WIN,award_win_rate)
    elseif result == constant.SYSTEM_STORE_WIN then     --从“小亏”和“大亏”中选择
        select_type = self:select_shape_type(line_shape,shape_common.SMALL_LOSE,shape_common.BIG_LOSE,system_lose_rate)
    elseif result == constant.FREE_TIMES then --免费次数
        select_type = shape_common.FREE_TIMES
    end
    
    --jackpot做特殊处理
    if select_type == shape_common.JACKPOT or select_type == shape_common.FREE_TIMES then
        local line_rate = global_configs.fruit_rand_shape.line_rate
        local award_lines = global_configs.fruit_rand_shape.award_lines
        local seven_rate 
        if select_type == shape_common.JACKPOT then
            seven_rate = global_configs.fruit_rand_shape.seven_rate
        else
            seven_rate = global_configs.fruit_rand_shape.bonus_rate
        end
        local caijin_rate = global_configs.tiger_rate.caijin_rate  
        local produce_rate = global_configs.fruit_rand_shape.shape_rate   --生成每张图的概率
        local total_weight_map = shape_common.get_total_weight(produce_rate)
        one_shape = shape_common.create_jackpot_shape(select_type,bet_lines,line_shape,line_rate,award_lines,seven_rate,caijin_rate,total_weight_map,produce_rate)
        lines,others = self:get_shape_info(one_shape,bet_lines)
        print("======find special type====",select_type,tostring_r(one_shape),tostring_r(lines),tostring_r(others))
        if not self:is_store_nomal(store_coins,others,result) then
            one_shape,lines,others = self:get_new_shape(bet_lines,store_coins,line_shape,result)
            print("==========special type not normal===",select_type,tostring_r(one_shape),tostring_r(lines),tostring_r(others))
        end 
    else
        local deal_special = false
        if select_type and line_shape[select_type] and #line_shape[select_type] > 0 then
            local rand = math_random(1,#line_shape[select_type])
            one_shape = line_shape[select_type][rand]
            lines,others = self:get_shape_info(one_shape,bet_lines)
            if not self:is_store_nomal(store_coins,others,result) then    --库存输钱到达警报值,从小亏和大亏中取
                select_type = self:select_shape_type(line_shape,shape_common.SMALL_LOSE,shape_common.BIG_LOSE,system_lose_rate)
                deal_special = true
            end
        end
        print("========select_type==",result,bet_lines,select_type)
        --随机生成一张,重新生成一张图时不能中奖金和免费次数
        if not select_type or not line_shape[select_type] or #line_shape[select_type] < 1 then  
            print("============not find in shapedata=====",result,bet_lines,select_type)
            one_shape,lines,others = self:get_new_shape(bet_lines,store_coins,line_shape,result)
        elseif deal_special then   --输钱超过警报值,从小亏和大亏中取
            print("=====deal special==",result,bet_lines,select_type)
            local rand = math_random(1,#line_shape[select_type])
            one_shape = line_shape[select_type][rand]
            lines,others = self:get_shape_info(one_shape,bet_lines)
            err_result = 1 
        end
    end

    curr_shape = {shape = one_shape,lines = lines,others = others}
    self:set_curr_shape(curr_shape,bet_lines)
    print("===============self.curr_shape======",result,tostring_r(self.curr_shape),err_result)
    return err_result
end

function TIGER_INDEX:get_uid_bet_record(uid)
    local player_record = self.player_betting_map[uid]
    local bet_lines = player_record.bet_lines
    local bet_score = player_record.bet_score or 0

    return bet_score,bet_lines
end

function TIGER_INDEX:get_game_result()
    assert(self.game_status == GAME_STATUS_OVER)
    return self.game_result
end

function TIGER_INDEX:judge_result()
    assert(self.game_status == GAME_STATUS_BETTING)
    local curr_shape = self.curr_shape

    self.game_result = {
        result_shape = curr_shape.shape,
        result_lines = curr_shape.lines,
        result_times = curr_shape.total_times,
        free_times = curr_shape.free_times,
        jackpot_rate = curr_shape.jackpot_rate,
        type = curr_shape.type
    }
    print("==============self.game_result==",tostring_r(self.game_result))

    self.game_status = GAME_STATUS_OVER
end

function M.new()
    local o = {}
    return setmetatable(o,{__index = TIGER_INDEX})
end

M.MAX_PLAYER_NUM         = MAX_PLAYER_NUM
M.MIN_PLAYER_NUM         = MIN_PLAYER_NUM

return M